iT邦幫忙

2023 iThome 鐵人賽

DAY 12
1
Software Development

由淺入深來探討Elasticsearch,從基礎語法到底層相關原理系列 第 12

【Day 12】由淺入深來探討Elasticsearch - 修改已建立的index結構與相關API

  • 分享至 

  • xImage
  •  

我們在開發上的各個階段,可能都需要對index做不同的調整
例如在測試過程,想要臨時在已經創建好的index做欄位的變更等等

今天我們就是著重在介紹遇到以下情形以及可能的開發需求時,對應處理的API

  1. 新增現有index的欄位
  2. 更改現有index的欄位類型
  3. 同一欄位有多種mapping
  4. 創建相似名稱索引時不用重新設定mapping

新增現有index的欄位
有時候你沒辦法保證索引的欄位數量不會再改動,並且因為ES本身預備很多彈性,我們在建立索引時的欄位不一定是最終版本,那我們要如何去為現有的索引增加欄位呢?

  1. 首先先創建一個索引並且只有name一個欄位,接著輸入一筆文檔
// 現有的test索引只有name一個欄位
PUT /test
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      }
    }
  }
}

PUT /test/_doc/1
{
  "name": "Eason"
}
  1. 接著輸入mapping API,內部直接接一個properties,並且把field name跟type一起輸入
PUT /test/_mapping
{
  "properties": {
    "new_field": {
      "type": "text"
    } 
  }
}
  1. 最後再get一次mapping就會發現成功新增一個欄位
GET /test/_mapping

https://ithelp.ithome.com.tw/upload/images/20230914/20161866ETTby6Xk2R.png

這時候可能會有疑問,這樣我們一開始輸入的文檔會有這個新欄位的預設值嗎?

{
  "_index": "test",
  "_id": "1",
  "_version": 1,
  "_seq_no": 0,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "name": "Eason"
  }
}

我們可以看到_source中沒有新的值,並且因為此時因為新建立欄位是null,無法建立反向索引並且做搜尋等操作

因此我們可以為index field添加默認值,方法有使用script語法跟使用pipeline
我們在後面的章節會再介紹pipeline,所以這邊用script示範

在ES中使用script時通常是要自定義一些項目時會使用到,但是性能可能會因此下降,因此如果非必要盡量不要使用
script的基本語法

"script": {
    "lang":   "...",
    "source" | "id": "...",
    "params": { ... }
}
  • lang表示你想使用的腳本語言,默認使用painless,在ES中具備性能以及安全性等等優勢
  • source就是腳本本身,或是假如你有寫好的腳本模板,這邊使用id進行調用
  • params就是傳遞給腳本的變量參數

那我們就讓new_field欄上補上默認值

  1. 首先使用update by query API
POST test/_update_by_query
  1. 補上script腳本
POST /test/_update_by_query
{
  "script": {
    "source": "if (ctx._source.new_field == null) {ctx._source.new_field = 'update!'}",
    "lang": "painless"
  }
}
  • 其中ctx在不同的使用情境下會有不同寫法,例如doc等。因為有太多種,要使用時去看官方文檔:https://www.elastic.co/guide/en/elasticsearch/painless/8.9/painless-contexts.html
  1. 我們回來看剛剛輸入的文檔
    https://ithelp.ithome.com.tw/upload/images/20230914/20161866VJuW8HhvTv.png
    這樣就成功了~

Reindex API
那如果我們今天創建了index,並且在有資料的情況下,能不能直接使用mapping API將欄位的type進行改變?
一般狀況下是不行的~
因為這樣必須要重新index,如果有幾百萬筆資料的話會非常花時間
我們可以使用Reindex API來更改現有index的欄位類型

  • 就是不同資源內的document複製到目標索引中
    • source to destination
  • 複製時如果文檔有欄位沒有在_source中的會被忽略掉
  • reindex不會將settings中的設定一起從source複製過去
    • mapping、shard等等需要先在destination index中設定好
  • 因為是不同索引,所以不會有版本衝突的問題

最基礎的就是如下:
設置好source與destination的index,切記這兩個index都要已經存在

POST /_reindex
{
  "source": {
    "index": "test-1"
  },
  "dest": {
    "index": "test-2"
  }
}

一些進階用法:

  1. 我們可以透過query的方式,來讓dest中只有我們想要的特定資料:
POST /_reindex
{
	"source": {
    "index": "test-1"
		"query": {
			"match": {}
		}
  },
  "dest": {
    "index": "test-2"
  }
}
  1. 透過指定_source的方式,留下我們想要的欄位,移掉那些我們用不到的
POST /_reindex
{
	"source": {
    "index": "test-1"
		"_source": ["column-1", "column-2"]
  },
  "dest": {
    "index": "test-2"
  }
}
  1. 透過slice的方式去限制我們要使用的部分
    a. 分為manual跟automatic寫法
// manual
POST /_reindex
{
  "source": {
    "index": "test-1",
    "slice": {
      "id": 0,
      "max": 2
    }
  },
  "dest": {
    "index": "test-2"
  }
}
// automatic
POST _reindex?slices=5&refresh
{
  "source": {
    "index": "test-1"
  },
  "dest": {
    "index": "test-2"
  }
}
  1. 組合拳:
    1. 從其他叢集過來
      • 此時不能使用slice
      • 可以多個source(當然一般在同一個cluster時也可以)
    2. 搭配query去篩選資料
    3. 使用routing來指定在dest索引上特定的shard,有幾種參數
      • keep:默認值,會按原有的routing設置
      • discard:會去掉原有的routing設置
      • =:直接在等號後面設置你的routing值
    4. 使用script來制定需求
      • 在下面的範例中,我想把source中content這個欄位改名為comment
POST /_reindex
{
"source": {
    "remote": {
      "host": "http://otherhost:9200",
      "username": "",
      "password": ""
    },
    "index": ["remote-index-2", "remote-index-2"],
    "_source": ["id", "name"],
    "query": {
      "match_all": {}
    }
}, "dest": {
"index": "local-dest-index",
"routing": "=user"
  },
  "script": {

    "source": """ctx._source.comment = ctx._source-remove("content")""",
    "lang": "painless"
  },
}

Multi-field mappings:
有時候同一個欄位的資料性質,可能需要多種方式去操作,有時候可能要用全文搜索去找,但是有時又想要能使用term-level query
例如車子的型號是:車廠 型號(ex: Honda CR-V)
我就可能需要使用term-level query去把所有Honda CR-V的車子找出來
但是同時我又想要單獨去找CR-V的型號

因此可以使用multi-fields

PUT /multi_field_test
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "desc": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

以上圖為例:

  • ES會為desc與desc.keyword建立各自的反向索引
  • desc可以用於全文搜索
  • desc.keyword(可自己命名),則可以使用term-level query
  • 甚至也可以在其中添加不同的analyzer進行調整

index template
之前有提過,ES很常會接收來自filebeat、metricbeat等的資料,或是常要接收一些log值

索引名稱可能如下:

access-log-2023-01
access-log-2023-02
access-log-2023-03

這時後我們就可以使用index template,創建符合特定條件名稱的索引,讓他們的mapping設定一樣的條件

  1. 使用index API,並且設定你的index template名
  2. 在index_patterns中設定哪些索引名是可以匹配的
  3. 設定該索引設定:包含settings, mappings等等
PUT /_template/access-logs
{
  "index_patterns": ["access-logs-*"],
  "settings": {
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "http.response.status_code": {
        "type": "long"
      }
    }
  }
}

此時我們直接創一個索引,並且直接看mapping

PUT /access-logs-2023-09
GET /access-logs-2023-09/_mapping

https://ithelp.ithome.com.tw/upload/images/20230914/20161866HRtyyq8Kgu.png
可以看到因為匹配到我們的模板,mapping直接設定好了

不過有以下幾點要注意:

  1. 創建索引跟索引模板如果設定上不衝突的話會並存,反之會以創建索引的規則為優先
  2. 如果創建索引時匹配到多個模板,會採用優先級最高的那個索引模板
  3. 如果更新模板索引的話,一樣是新創的索引才吃的到設定

今天的內容一樣非常非常多

  1. 新增現有索引,使其有新的欄位
    • mapping API
    • 要讓其有默認值的話,需要使用script來操作
  2. Reindex API,可以將舊有索引的文檔匯入到目標索引中
  3. 可以為單一欄位添加不同的mapping配置
  4. index template可以幫助我們在相似名稱的索引創建時能直接使用相同的mapping

我們大致介紹了mapping的類型
並且也說明了,如果我們建立好mapping跟index,當需求改變時
我們可以透過一些API來幫助我們去新增欄位,或是轉移資料到新索引上

但是在一開始我們可能對於整個專案業務不熟悉的狀況下
我們甚至可能對於我們是不是要再細分處理原有的欄位
或是對於送進索引的row data,要不要再分割成更適合我們需求的欄位也不太清楚
並且可能我們也不想要使用額外的空間去儲存這些暫時性的資料,那應該怎麼做?

明天來介紹Runtime fields,這個在開發初期也算是很好用的功能,並可以解決上面的問題
也介紹在實務上通常會怎麼使用

參考資料
script:
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html
reindex API:
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html
index templates:
https://www.elastic.co/guide/en/elasticsearch/reference/current/index-templates.html


上一篇
【Day 11】由淺入深來探討Elasticsearch - Dynamic templates
下一篇
【Day 13】由淺入深來探討Elasticsearch - Runtime fields
系列文
由淺入深來探討Elasticsearch,從基礎語法到底層相關原理30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言